
%include "boot.inc"

section loader vstart=LOADER_BASE_ADDR
LOADER_STACK_TOP equ LOADER_BASE_ADDR

; 在 mbr.S 中: jmp LOADER_BASE_ADDR + 0x300
;jmp loader_start ; 占用两个字节
; 

; 构建 GDT 及其内部的描述符
GDT_BASE:   dd 0x0000000
            dd 0x0000000

CODE_DESC:  dd 0x0000FFFF
            dd DESC_CODE_HIGH4

DATA_STACK_DESC:    dd 0x0000FFFF
                    dd DESC_DATA_HIGH4

VIDEO_DESC: dd 0x80000007
            dd DESC_VIDEO_HIGH4

GDT_SIZE equ $ - GDT_BASE
GDT_LIMIT equ GDT_SIZE - 1
times 60 dq 0
SELECTOR_CODE  equ (0x0001 << 3) + TI_GDT + RPL0
SELECTOR_DATA  equ (0x0002 << 3) + TI_GDT + RPL0
SELECTOR_VIDEO equ (0x0003 << 3) + TI_GDT + RPL0

; 当前偏移 loader.bin 文件头为 0x200h, loader.bin 的加载地址为 0x900, 故 total_mem_bytes 内存中的地址是 0xb00.
; 将来的内核中会引用此地址
total_mem_bytes dd 0 ; 用于保存内存容量，字节单位

gdt_ptr dw GDT_LIMIT
        dd GDT_BASE

; 人工对齐：total_mem_bytes(4) + gdt_ptr(6) + ards_buf(244) + ards_nr2(2) = 256 字节
ards_buf times 244 db 0
ards_nr dw 0

loader_start:
    xor ebx, ebx        ; 下一个 ARDS 的地址, 首次需置为 0
    mov edx, 0x534d4150 ; 固定的签名标记
    mov di, ards_buf    ; es:di ARDS 缓冲区，保存读入的内存信息
.e820_mem_get_loop:
    mov eax, 0x0000e820 ; 子功能号
    mov ecx, 20         ; ARDS 结构的字节大小
    int 0x15
    jc .e820_failed_so_try_e801 ; CF=0为未出错，否则调用出错
    add di, cx          ; 指向下一个 ARDS
    inc word [ards_nr]  ; 记录 ARDS 的数量
    cmp ebx, 0          ; 若 ebx=0, 则表示当前是最后一个 ARDS
    jnz .e820_mem_get_loop

; 找出所有 ARDS 中内存容量最大的
    mov cx, [ards_nr]
    mov ebx, ards_buf
    xor edx, edx
.find_max_mem_area:
    mov eax, [ebx]      ; baseAddLow
    add eax, [ebx + 8]  ; lengthLow
    add ebx, 20         ; 指向缓冲区中下一个 ARDS
    cmp edx, eax        ; 冒泡排序，直到找到最大
    jge .next_ards
    mov edx, eax        ; edx 为总内存大小
.next_ards:
    loop .find_max_mem_area
    jmp .mem_get_ok

;----------------------------------
; int 15h
; 功能号：AX=E801H
; 获取内存大小，最大支持 4G
;----------------------------------
.e820_failed_so_try_e801:
    mov ax, 0xe801
    int 0x15
    jc .e801_failed_so_try88

    mov cx, 0x400
    mul cx ; 乘 1024 变成 MB
    shl edx, 16
    and eax, 0x0000FFFF
    or edx, eax
    add edx, 0x100000
    mov esi, edx

    xor eax, eax
    mov ax, bx
    ;mov ecx, 0x10000
    mov ecx, 0x400
    mul ecx
    add esi, eax ; 高 32 位 edx 必然为 0，因此只加 eax 即可
    mov edx, esi
    jmp .mem_get_ok

; --------------------------------------------
; int 15h, 功能号：AH=0x88h
; 获取内存大小，只能获取 64M 之内
; --------------------------------------------
.e801_failed_so_try88:
    mov ah, 0x88
    int 0x15
    jc .error_hlt
    and eax, 0x0000FFFF

    mov cx, 0x400
    mul cx
    shl edx, 16
    or edx, eax
    add edx, 0x100000

.mem_get_ok:
    ;mov ebp, total_mem_bytes
    mov [total_mem_bytes], edx

; -----------------进入保护模式--------------------

    in al, 0x92
    or al, 0000_0010B
    out 0x92, al

    lgdt [gdt_ptr]

    mov eax, cr0
    or eax, 0x00000001
    mov cr0, eax

    jmp dword SELECTOR_CODE:p_mode_start

.error_hlt:
    hlt

[bits 32]
p_mode_start:
    mov ax, SELECTOR_DATA
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov esp, LOADER_STACK_TOP
    mov ax, SELECTOR_VIDEO
    mov gs, ax

    mov byte [gs:160], 'P'

    jmp $
    
